// functions

// abbr.
// a (arr): array
// f (func): function

// basic functions:
function id(x) {
	return x;
}

function add(x, y) {
	// if (arguments.length == 1) {y=1;}
	return x + y;
}

function inc(x) {
	// if (arguments.length == 0) {return 1;}
	return ++x;
}


function sub(x, y) {
	// if (arguments.length == 1) {y=1;}
	return x - y;
}

function dec(x) {
	// if (arguments.length == 0) {return 1;}
	return --x;
}


function multiply(x, y) {
	// if (arguments.length == 1) {y=x;}
	return x * y;
}


function div(x, y) {
	// if (arguments.length == 1) {y=1;}
	return x / y;
}


function isPositive(x){
	return x>0;
}

function isNegative(x){
	return x>0;
}

function isNonzero(x){
	return x!=0;
}

function isZero(x){
	return x===0;
}

function is(x, y){
	if (arguments.length==1){
	return function(y){return x===y;};
    }
	else{
		return x === y;
	}
}

function isnot(x, y){
	if (arguments.length==1){
	return function(y){return x!==y;};
    }
	else{
		return x !== y;
	}
}

function eq(x, y){
	if (arguments.length==1) {
	return function(y) {return x==y;} ;
    }
	else {
		return x == y;
	}
}

function neq(x, y){
	if (arguments.length==1){
	return curry(neq, x)
	}
	else{
		return x != y;
	}
}

function pass(){};

// curryging:
function curry(f, x) {
	// currying
    // f(x): y|-> f(x,y)
	return function(y) {return f(x, y);};
}


function curry2(f, y) {
    // f(y): x|-> f(x,y)
	return function(x) {return f(x, y);};
}


function currying(f){
    // f(x, y) or curry(f, x)
	function g(x, y){
		if (arguments.length==1){
			return function(y){f(x, y);};
		}
		else{
			return f(x, y);
		}
	}
    return g;
}

function apply(f, a){
	return f.apply(null, a);}

// mapping
function mapi(f, a) {
	var b =[];
	for (var i=0; i < a.length; i++)
		{b.push(f(a[i], i));}
	return b;
}

function mapfs(fs, a) {
	// [fi(ai)]
	f = function(x, i) {return fs[i](x)}; 
	return mapi(f, a);
}

// functions of array
function count(a, key, start, end){
	var arity = arguments.length;
	if (arity<=3){
		end = a.length;
		if (arity<=2){
			start = 0;
			if (arity<=1){
		        key = isNonzero;}}}
    var n =0;
	for (var i=start; i<end; i++){
		if (key(a[i])) {
			n++;
		}
	}
	return n;
}


function complementary(a1, a2){
	var b=[];
  for (var i=0; i<a1.length; i++){
  	if (! a1[i] in a2){
  		b.push(a1[i]);
  	}
  }
  return b;
}


function find(a, key, start, end){
	// find the index of the first element in a that key(*)==true
	var arity = arguments.length;
	if (arity<=3){
		var end = a.length;
		if (arity<=2){
			var start = 0;
			if (arity<=1){
		        key = isNonzero;}}}
	for (var i=start; i<end; i++){
		if (key(a[i])){
			return i;
		}
	}
	return false;
}


function isempty(arr){
	return arr.length === 0;
}


function flat(a){
	if (a instanceof Array){
		var b=[];
        for (var i=0; i<a.length; i++){
        	var ai=a[i];
        	if (ai instanceof Array){
                b = b.concat(flat(ai));
        	}else{
        		b.push(ai);
        	}
        }
        return b;
	}else{
		return a;}
}

// generate array
function toVector(len, f) {
	if (arguments.length==1)
		{f=id;}
	var a=[];
	for (var i=0; i < len; i++)
		{a.push(f(i));}
	return a;
}

toList = toVector;

function toMatrix(m, n, f) {
	if (arguments.length<=2){
		f=add;
		if (arguments.length<=1){
			n = m;
		}}
	var a=[];
	for (var i=0; i < m; i++){
		var b=[]; 
		for (var j=0; j < n; j++){
			b.push(f(i, j));}
	    a.push(b);
	}
	return a;
}

// function toArray(ns, f, x){
	// arity of f is ns.length
//	if (ns.length===2){return toMatrix(ns[0], ns[1], function(x,y){return f([x,y]);});}
//	F=function(x){return toArray(apply(f, [x].concate(ns.slice(1))));};
//	return toVector(ns[0],F);}

function range(start, stop, step){
	if (arguments.length<=2)
		{step = 1;}
	var a=[];
	if (step > 0){
	for (var i=start; i < stop; i+=step)
		{a.push(i);}
    }
	else{
		for (var i=start; i > stop; i+=step)
		{a.push(i);}
	}
	return a;
}

function linspace(start, stop, N){
	// N >= 2
	if (arguments.length<=2)
		{N = 100;}
	var a=[], step = (stop - start) / (N-1);
	for (var i=start; i <= stop; i+=step)
		{a.push(i);}
	return a;
}

function eqnodes(start, stop, N){
	// N >= 1
	if (arguments.length<=2)
		{N = 100;}
	var a=[], step = (stop - start) / N;
	for (var i=start; i <= stop; i+=step)
		{a.push(i);}
	return a;
}

function slice(a, start, stop, step){
	// slice of array
	var arity = arguments.length;
	if (arity<=3)
		{return a.slice(start, stop);  //step = 1; 
	    if (arity<=2)
	    	{return a.slice(start);  // stop = a.length;
	    		if (arity<=1)
	    			{return s.slice(); //start = 0;
	    			}}}
	var b=[];
	if (step > 0){
	for (var i=start; i < stop; i+=step)
		{b.push(a[i]);}}
	else{
		for (var i=start; i > stop; i+=step)
		{b.push(a[i]);}
	}
	return b;
}

// copy
function deepcopy(a){
	// a is an array
	var b=[];
	for (var i=0; i < a.length; i++) {
		var ai = a[i];
		if (ai instanceof Array) {
			b.push(deepcopy(ai));
		}else{  // a[i]
			b.push(ai);
		}
	}
	return b;
}

// function funcall(){
// 	this.func = arguments[0];
// 	this.arguments = arguments.slice(1);
// 	this.value = 
// }


function str2mat(s){
  return s.split(';').map(function(x){return x.split(' ').map(parseFloat);});
}


function parseMatrix(s){
  return s.trim().split(/[;\\]+/).map(function(x){return x.trim().split(/[ ,&]+/).map(parseFloat);});
}